package orbito.ui;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Collection;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.Timer;

import orbito.DefaultGameController;
import orbito.GameController;
import orbito.GameSettings;
import orbito.graphics.ViewFocus;
import orbito.scene.SceneGraph;

@SuppressWarnings("serial")
public class OrbitoFrame extends JFrame implements KeyListener, MouseListener, ActionListener, ComponentListener {
	
	// The game controller.
	GameController game;
	
	// The game canvas.
	GameCanvas canvas;
	
	// The animation timer to periodically repaint the game canvas.
	Timer animationTimer;
	
	// -------------------------------------------
	// The UI elements.
	JMenuBar menuBar;
	JMenu fileMenu, simMenu, viewMenu;
	
	JMenuItem fileExitItem;
	JMenuItem simPlayItem, simPauseItem, simSlowerItem, simFasterItem, simResetItem, simReseedItem;
	JMenuItem viewSelectFocusItem, viewClearFocusItem, viewSelectSceneItem, viewClearSceneItem;
	
	JCheckBoxMenuItem simDtUpLockItem, simDtDownLockItem, antiAliasItem;
	
	public OrbitoFrame() {
		this.setTitle("Orbito");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setResizable(true);
		
		// Init the UI.
		this.initUi();
		
		// Init the simulation.
		this.init();
		
		// Pack it, and center on screen.
		this.pack();
		this.setCenter();
		
		// Start everything.
		this.start();
	}
	
	private void setCenter() {
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		Dimension dim = toolkit.getScreenSize();
		int x = (int) (dim.getWidth() - this.getWidth()) / 2;
		int y = (int) (dim.getHeight() - this.getHeight()) / 2;
		this.setLocation(x, y);
	}
	
	private void buildViewSelectFocusItem(JMenuItem menu, List<ViewFocus> focusItems) {
		if (menu == viewSelectFocusItem) {
			menu.removeAll();
		}
		
		if (focusItems == null || focusItems.isEmpty()) {
			menu.add(new JMenuItem("None available"));
		} else {
			for (ViewFocus f : focusItems) {
				JMenuItem menuItem, submenuItem;
				
				if (!f.getFocusChildren().isEmpty()) {
					menuItem = new JMenu(f.getFocusName());
					submenuItem = new ViewFocusMenuItem(f.getFocusName(), f);
					submenuItem.addActionListener(this);
					menuItem.add(submenuItem);
					buildViewSelectFocusItem(menuItem, f.getFocusChildren());
				} else {
					menuItem = new ViewFocusMenuItem(f.getFocusName(), f);
					menuItem.addActionListener(this);
				}
				
				menu.add(menuItem);
			}
		}
	}
	
	private void buildViewSelectSceneItem(JMenuItem menu, Collection<SceneGraph> scenes) {
		if (menu == viewSelectSceneItem) {
			menu.removeAll();
		}
		
		if (scenes == null || scenes.isEmpty()) {
			menu.add(new JMenuItem("None available"));
		} else {
			for (SceneGraph s : scenes) {
				JMenuItem menuItem = new SceneMenuItem(s.getSceneName(), s);
				menuItem.addActionListener(this);
				menu.add(menuItem);
			}
		}
	}
	
	private ImageIcon createIcon(String path) {
		return new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(path)));
	}
	
	private void initUi() {
		// Init the input store and set listeners.
		InputStore.init();
		InputStore inputListener = new InputStore();
		
		this.addKeyListener(inputListener);
		this.addKeyListener(this);
		
		// Create the animator canvas.
		canvas = new GameCanvas(AppSettings.INIT_CANVAS_WIDTH, AppSettings.INIT_CANVAS_HEIGHT);
		canvas.addMouseListener(inputListener);
		canvas.addMouseListener(this);
		canvas.addComponentListener(this);
		this.add(canvas);
		
		// Create animation timer to repaint game canvas.
		animationTimer = new Timer(1000 / (int) GameSettings.desiredFramerate, new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				canvas.repaint();
			}
		});
		
		// --------------------
		// Create Menu.
		
		// Create the menu bar.
		this.menuBar = new JMenuBar();
		this.setJMenuBar(menuBar);
		
		// file
		this.fileMenu = new JMenu("File");
		this.menuBar.add(fileMenu);

		// file, exit
		this.fileExitItem = new JMenuItem("Exit", createIcon("/icons/door_out.png"));
		this.fileExitItem.addActionListener(this);
		this.fileMenu.add(fileExitItem);
		
		// simulator
		this.simMenu = new JMenu("Simulation");
		this.menuBar.add(simMenu);
		
		// simulator, play
		this.simPlayItem = new JMenuItem("Play", createIcon("/icons/control_play_blue.png"));
		this.simPlayItem.addActionListener(this);
		this.simMenu.add(simPlayItem);
		
		// simulator, pause
		this.simPauseItem = new JMenuItem("Pause", createIcon("/icons/control_pause_blue.png"));
		this.simPauseItem.addActionListener(this);
		this.simMenu.add(simPauseItem);
		
		// simulator, slower
		this.simSlowerItem = new JMenuItem("Slower", createIcon("/icons/control_rewind_blue.png"));
		this.simSlowerItem.addActionListener(this);
		//this.simMenu.add(simSlowerItem);
		
		// simulator, faster
		this.simFasterItem = new JMenuItem("Faster", createIcon("/icons/control_fastforward_blue.png"));
		this.simFasterItem.addActionListener(this);
		//this.simMenu.add(simFasterItem);
		
		// simulator, reset
		this.simResetItem = new JMenuItem("Reset", createIcon("/icons/control_start_blue.png"));
		this.simResetItem.addActionListener(this);
		this.simMenu.add(simResetItem);
		
		this.simMenu.addSeparator();
		
		// simulator, enforce dt
		this.simDtUpLockItem = new JCheckBoxMenuItem("Dt Up-lock");
		this.simDtUpLockItem.addActionListener(this);
		this.simMenu.add(simDtUpLockItem);
		
		// simulator, limit dt
		this.simDtDownLockItem = new JCheckBoxMenuItem("Dt Down-lock");
		this.simDtDownLockItem.addActionListener(this);
		this.simMenu.add(simDtDownLockItem);
		
		this.simMenu.addSeparator();
		
		// simulator, reseed
		this.simReseedItem = new JMenuItem("Reseed", createIcon("/icons/palette.png"));
		this.simReseedItem.addActionListener(this);
		this.simMenu.add(simReseedItem);
		
		// view
		this.viewMenu = new JMenu("View");
		this.menuBar.add(viewMenu);
		
		// view, select focus
		this.viewSelectFocusItem = new JMenu("Select focus");
		this.viewSelectFocusItem.setIcon(createIcon("/icons/magnifier_zoom_in.png"));
		buildViewSelectFocusItem(viewSelectFocusItem, null);
		this.viewMenu.add(viewSelectFocusItem);
		
		// view, clear focus
		this.viewClearFocusItem = new JMenuItem("Clear focus", createIcon("/icons/magifier_zoom_out.png"));
		this.viewClearFocusItem.addActionListener(this);
		this.viewMenu.add(viewClearFocusItem);
		
		this.viewMenu.addSeparator();
		
		// view, select scene
		this.viewSelectSceneItem = new JMenu("Load scene");
		this.viewSelectSceneItem.setIcon(createIcon("/icons/map.png"));
		buildViewSelectSceneItem(viewSelectSceneItem, null);
		this.viewMenu.add(viewSelectSceneItem);
		
		// view, clear scene
		this.viewClearSceneItem = new JMenuItem("Clear scene", createIcon("/icons/map_delete.png"));
		this.viewClearSceneItem.addActionListener(this);
		this.viewMenu.add(viewClearSceneItem);
		
		this.viewMenu.addSeparator();
		
		// view, anti-alias
		this.antiAliasItem = new JCheckBoxMenuItem("Anti-alias");
		this.antiAliasItem.addActionListener(this);
		this.viewMenu.add(antiAliasItem);
	}
	
	private void init() {
		// Create a new game object.
		game = new DefaultGameController(this);
		game.updateViewport(canvas.getWidth(), canvas.getHeight());
		canvas.setGameController(game);
		
		// Init the game.
		game.init();
	}
	
	private void start() {
		// Start the timers.
		animationTimer.start();
		
		// Start the game.
		game.start();
	}
	
	public void updateMenuUi() {
		// --------------------
		// Update sim menu.
		this.simMenu.setEnabled(game.getCurrentScene() != null);
		this.simPlayItem.setVisible(game.isPaused());
		this.simPauseItem.setVisible(!game.isPaused());
		this.simDtUpLockItem.setState(game.isDtUpLock());
		this.simDtDownLockItem.setState(game.isDtDownLock());
		
		// --------------------
		// Update view menu.
		this.viewClearFocusItem.setEnabled(game.getCurrentViewFocus() != null);
		this.viewClearSceneItem.setEnabled(game.getCurrentScene() != null);
		this.antiAliasItem.setState(GameSettings.performAntiAlias);
	}
	
	public void updateViewFocusUi() {
		buildViewSelectFocusItem(viewSelectFocusItem, game.getViewFocusItems());
	}
	
	public void updateScenesUi() {
		buildViewSelectSceneItem(viewSelectSceneItem, game.getScenes());
	}
	
	public void keyPressed(KeyEvent e) {
		// --------------------
		// Simulator settings.
		
		// PAUSE: Toggle paused state.
		if (e.getKeyCode() == KeyEvent.VK_PAUSE) {
			if (game.isPaused()) {
				game.unpause();
			} else {
				game.pause();
			}
		}
		
		// R|SUBTRACT: Decrease time compression.
		else if (e.getKeyCode() == KeyEvent.VK_R || e.getKeyCode() == KeyEvent.VK_SUBTRACT) {
			game.decreaseTimeCompression();
		}
		
		// T|ADD: Increase time compression.
		else if (e.getKeyCode() == KeyEvent.VK_T || e.getKeyCode() == KeyEvent.VK_ADD) {
			game.increaseTimeCompression();
		}
		
		// F11: Reset game.
		else if (e.getKeyCode() == KeyEvent.VK_F11) {
			game.resetCurrentScene();
		}
		
		// F12: Reseed and reset.
		else if (e.getKeyCode() == KeyEvent.VK_F12) {
			game.seed(System.nanoTime());
			game.resetCurrentScene();
		}
		
		// --------------------
		// View settings.

		// F1: Toggle camera info.
		if (e.getKeyCode() == KeyEvent.VK_F1) {
			GameSettings.renderCameraInfo = !GameSettings.renderCameraInfo;
		}
		
		// F2: Toggle sim info.
		else if (e.getKeyCode() == KeyEvent.VK_F2) {
			GameSettings.renderSimInfo = !GameSettings.renderSimInfo;
		}
		
		// F3: Toggle planetoid info.
		else if (e.getKeyCode() == KeyEvent.VK_F3) {
			GameSettings.planetoidInfoDetail = (GameSettings.planetoidInfoDetail + 1) % 4;
		}
		
		// F4: Toggle planetoid trails.
		else if (e.getKeyCode() == KeyEvent.VK_F4) {
			GameSettings.renderPlanetoidTrails = !GameSettings.renderPlanetoidTrails;
		}
		
		// Update the menu UI.
		updateMenuUi();
	}

	public void keyReleased(KeyEvent e) {}
	public void keyTyped(KeyEvent e) { }

	public void mouseClicked(MouseEvent e) { }
	public void mouseEntered(MouseEvent e) { }
	public void mouseExited(MouseEvent e) { }
	public void mousePressed(MouseEvent e) { }
	public void mouseReleased(MouseEvent e) { }

	public void actionPerformed(ActionEvent e) {
		// --------------------
		// File menu.
		
		// Exit the application.
		if (e.getSource() == fileExitItem) {
			System.exit(0);
		}
		
		// --------------------
		// Simulator menu.
		
		// Play button.
		else if (e.getSource() == simPlayItem) {
			if (game.isPaused()) {
				game.unpause();
			}
		}
		
		// Pause button.
		else if (e.getSource() == simPauseItem) {
			if (!game.isPaused()) {
				game.pause();
			}
		}
		
		// Slower button.
		else if (e.getSource() == simSlowerItem) {
			game.decreaseTimeCompression();
		}
		
		// Faster button.
		else if (e.getSource() == simFasterItem) {
			game.increaseTimeCompression();
		}
		
		// Reset button.
		else if (e.getSource() == simResetItem) {
			game.resetCurrentScene();
		}
		
		// Reseed button.
		else if (e.getSource() == simReseedItem) {
			game.seed(System.nanoTime());
			game.resetCurrentScene();
		}
		
		// Dt up-lock checkbox.
		else if (e.getSource() == simDtUpLockItem) {
			game.setDtUpLock(simDtUpLockItem.getState());
		}
		
		// Dt down-lock checkbox.
		else if (e.getSource() == simDtDownLockItem) {
			game.setDtDownLock(simDtDownLockItem.getState());
		}
		
		// --------------------
		// View menu.
		
		// Select focus button.
		else if (e.getSource() instanceof ViewFocusMenuItem) {
			ViewFocus focus = ((ViewFocusMenuItem) e.getSource()).getViewFocus();
			game.setCurrentViewFocus(focus);
		}
		
		// Clear focus button.
		else if (e.getSource() == viewClearFocusItem) {
			game.clearCurrentViewFocus();
		}
		
		// Select scene button.
		else if (e.getSource() instanceof SceneMenuItem) {
			SceneGraph scene = ((SceneMenuItem) e.getSource()).getScene();
			game.loadScene(scene);
		}
		
		// Clear scene button.
		else if (e.getSource() == viewClearSceneItem) {
			game.clearCurrentScene();
		}
		
		// Anti-alias checkbox.
		else if (e.getSource() == antiAliasItem) {
			GameSettings.performAntiAlias = antiAliasItem.getState();
		}
		
		// Update the menu UI.
		updateMenuUi();
	}
	
	public void componentShown(ComponentEvent e) { }
	public void componentHidden(ComponentEvent e) { }
	public void componentMoved(ComponentEvent e) { }
	
	public void componentResized(ComponentEvent e) {
		if (e.getSource() == canvas) {
			game.updateViewport(canvas.getWidth(), canvas.getHeight());
		}
	}
}
